Глава 14

Разработка пользовательских тегов

На сегодняшний день в ColdFusion входит более семидесяти встроенных тегов, позволяющих создавать полноценные Web-приложения. Также сообщество разработчиков, использующих ColdFusion, пpeредлагает набор пользовательских тегов для свободного распространения, с которыми можно ознакомиться и при необходимости скачать из Internet по адресу http://devex.allaire.com/DeveIoper/Gallery/CustomTags.cfm.

Впрочем, почему бы нам не научиться создавать собственные пользовательские теги, тем более что это не требует громадных усилий. Пользовательский тег представляет собой некий код с возможностью его многократного использования, и поведение которого может зависеть от передаваемых атрибутов.

Применение пользовательских тегов позволяет ускорить процесс разработки Web-приложений и облегчить сопровождение будущего продукта.

Пользовательские теги в ColdFusion могут быть двух видов:

Создание CF-тегов

Как уже было отмечено, CF-теги создаются на основе CFML-страниц и их имена записываются с приставкой CF_. Например, для вызова тега, размещенного на странице MyFirstTag.cfm, необходимо использовать следующую конструкцию:

<CF_MyFirstTag>

Пользовательский тег может также записываться с указанием закрывающего тега, например:

<CF_MyFirstTag>

<TABLE border="0"><tr>

<TD bgcolor="Green"><FONT color="White">Hello!</FONT></TD> </TR></TABLE> </CF_MyFirstTag>

Размещать пользовательский тег следует либо там, где располагается страница, вызывающая его, либо в специально предусмотренном для этой цели каталоге. По умолчанию каталог для хранения пользовательских тегов находится на X:\Cfusion\CustomTags, где X — устройство, на которое был предустановлен ColdFusion Server. Но это не мешает изменять существующий путь или добавлять новые пути для размещения и в дальнейшем обнаружения системой пользовательских тегов. На рис. 14.1 показана страница администратора "ColdFusion Administrator" открытая в разделе Server | Extensions | Custom Tag Paths (Сервер Расширения | Путь к пользовательским тегам), где предоставляется возможность регистрировать новые пути для пользовательских тегов.

Рис. 14.1. Страница "ColdFusion Administrator", раздел Custom Tag Paths

При вызове тега также можно передавать значения переменных в качестве атрибутов. Например, предположим, что необходимо создать пользовательский тег по преобразованию температуры воздуха из значений по шкале Цельсия (С) в градусы по Фаренгейту (F) и наоборот. Предварительно для данного примера определим синтаксис нового тега:

<CF_TemperatureTransform

Value="Number" [CtoF="Boolean"]>

где Value — числовое значение, требуемое для преобразования; ctoF — логическое значение, указывающее направление преобразования. Причем атрибут value является обязательным в отличие от атрибута ctoF. По умолчанию направление преобразования происходит из (С) в (F) впрочем, так же, как и при указании атрибута равным I, Yes, True или просто при упоминании данного атрибута без указания значения.

Теперь, когда задача определена и сформулированы все условия, можно приступить к написанию страницы TemperatureTransform.cfm (листинг 14.1).

Листинг 14.1. Код страницы TemperatureTransform.cfm

<!--- Изменение температуры (Transformation of temperature) --->

<CFIF isDefined("Attributes.Value")>

<CFIF isDefined("Attributes.CtoF")>

<CFIF isNumeric(Attributes.Value) AND isBoolean(Attributes.CtoF)>

<CFIF YesNoFormat(Attributes.CtoF) equal "Yes">

<CFSET Caller.Temperature = Attributes.Value*1.8+32 & " F">

<CFELSE>

<CFSET Caller.Temperature = (Attributes.Value-32)*5/9 & " C"> </CFIF>

<CFELSE>

<CFSET Caller.Temperature = "Errorl"> </CFIF> <CFELSE>

<CFSET Caller.Temperature = Attributes.Value*1.8+32 & " F"> </CFIF>

<CFELSE>

<CFSET Caller.Temperature = "Error2">

</CFIF>

После чего остается только использовать новый тег. Например, так:

<CF_TemperatureTransform Value = "21">

<CFOUTPUT>Temperature: iVariables.Temperaturel</CFOUTPUT>

В результате будет выведена следующая строка:

Temperature: 69.8 F

Если есть необходимость преобразования в обратную сторону, можно применить наш тег следующим образом:

<CF_TemperatureTransform Value = "69.8" CtoF="No">

<CFOUTPUT>Temperature: #Variables.Temperaturel</CFOUTPUT>

Что позволит получить следующий результат:

Temperature: 21 С

В приведенном выше примере были использованы переменные атрибутов пользовательских тегов Attributes, позволяющие анализировать передаваемые атрибуты в теле пользовательского тега, и также применялись переменные источников вызова caller для передачи переменных из шаблонов пользовательских тегов ColdFusion.

Также можно указывать вложение тегов, т. е. когда в вызываемом шаблоне пользовательского тега происходит обращение к другому пользовательскому тегу. Например, следующим шагом мы несколько модернизируем наш пример преобразования температуры.

В нашем примере при операции перевода значения температуры может возникнуть ситуация, когда значение после запятой достигнет немыслимых размеров. Предположим, если перевести по нашей формуле 70° по Фаренгейту в значение по шкале Цельсия, то в результате получим:

Temperature: 21.1111111111 С

Что является не очень удачным результатом. Для решения этой проблемы, в общем-то, достаточно воспользоваться существующей в ColdFusion функцией NumberFormat () . Однако мы "усложним" себе задачу и создадим еще один пользовательский тег для форматирования числовых величин (листинг 14.2).

Синтаксис пользовательского тега, позволяющего выполнить форматирование числовых величин, таков:

<CF_NumFormat

Value = "Number" Format = "Mask">

где Value — числовое значение, требуемое для форматирования; Format -маска, определяющая формат и соответствующая правилу задания маски для функции ColdFusion NumberFormat () .

Листинг 14.2. Код страницы NumFormat.cfm

<CFIF isDefined("Attributes.Value") AND isDefined("Attributes.Format")>

<CFSET Caller.Num = NumberFormat(Attributes.Value, Attributes.Format)>

<CFELSE>

<CFSET Caller.Num = NumberFormat(0, "___._")>

</CFIF>

Теперь, когда мы обладаем таким замечательным пользовательским тегом, как CF_NumFormat, его можно использовать следующим образом:

<CF_NumFormat Value = "10.123" Format = "__._">

<CFOUTPUT>#Variables.Num#</CFOUTPUT>

В результате получим число:

10.1

Далее представим обновленный код пользовательского тега для преобразования температуры (листинг 14.3), где используется вложение пользовательского тега <CF_NumFormat>.

Листинг 14.3. Код страницы Temperature!ransform2.cfm

<!--- Transformation of temperature Version2 --->

<CFSET Caller.Temperature = "">

<CFIF isDefined("Attributes.Value")>

<CFIF isDefined("attributes.CtoF")>

<CFIF isNumeric(Attributes.Value) AND isBoolean(Attributes.CtoF)>

<CFIF YesNoFormat(Attributes.CtoF) equal "Yes">

<CFSET Val = Attributes.Value*1.8+32>

<CFSET Measurement = " F"> <CFELSE>

<CFSET Val = (Attributes.Value-32)*5/9>

<CFSET Measurement = " C"> </CFIF>

<CFELSE>

<CFSET Caller.Temperature = "Error1"> </CFIF>

<CFELSE>

<CFSET Val = Attributes.Value*1.8+32>

<CFSET Measurement = " F"> </CFIF>

<CFELSE>

<CFSET Caller.Temperature = "Error2"> </CFIF>

<CFIF Caller.Temperature contains "Error">

<CFELSE>

<CF_NumFormat Value = "#Val#" Format = "___._" >

<CFSET Caller.Temperature = #Variables.Numl & "#Measurement#">

</CFIF>

Возвращаясь к нашей проблеме, переведем все те же 70° по Фаренгейту в значение по шкале Цельсия, как показано ниже:

<CF_TemperatureTransform2 Value = "70" CtoF = "No">

<CFOUTPUT>Temperature: #Variables.Temperaturel</CFOUTPUT>

В результате получим такой результат:

Temperature: 21.1 С

При использовании вложенных тегов применяются функции, позволяющие получать доступ к наследственным данным:

К примеру, если тег <CF_NumFormat> дополнить строкой Кода:

<CFOUTPUT>#GetBaseTagList()#</CFOUTPUT>

то при прямом обращении результатом будет являться значение, представленное ниже:

CFOUTPUT,CFIF,CF_NUMFORMAT

При обращении к тегу <CF_NumFormat> внутри пользовательского тега <CF_TemperatureTransform2> как к вложенному тегу, результатом будет значение:

CFOUTPUT,CFIF,CF_NUMFORMAT,

CFELSE,CF_TEMPERATURETRANSFORM2

Обработав полученное значение, можно "вычленить" всех имеющихся "родителей" данного тега.

Как уже было отмечено в одном из первых примеров, пользовательский тег может записываться с указанием закрывающего тега, при этом ColdFusion позволяет отслеживать начало и окончание выполнения пользовательского тега. Определить же способ выполнения тега можно посредством переменной ExecutionMode, используя при этом относительную ссылку ThisTag, как показано в следующем примере:

<CFSWITCH EXPRESSION=#ThisTag.ExecutionModet>

<CFCASE VALUE = 'Start'> <!---Начало тега--->

</CFCASE>

<CFCASE VALUE = 'End'>

<!---Конец тега--->

</CFCASE> </CFSWITCH>

Здесь блок, условно назовем его start, выполняется в начале тега, соответственно блок End выполняется при указании окончания тега.

Для демонстрации применения пользовательских тегов с указанием окончания приведем пример тега, позволяющего преобразовывать текст в табличную форму с возможностью изменения характеристик данной таблицы и осуществлять исправление некоторых грамматических ошибок, допущенных в тексте. Назовем этот тег <CF_MyTable>.

Синтаксис тега <CF_муТаblе>:

<CF_MyTable

Width = "Number"

Border = "Number"

CellPadding = "Number"

Cellspacing = "Number"

TBGColor = "Color"

TColor = "Color"

TSize = "Number"

TText = "String">

Текст... </CF_MyTable>

где:

Далее представим код пользовательского тега <CF_MyTabie> (листинг 14.4).

Листинг 14,4. Код страницы MyTable.cfm

<!----My Table---->

<CFSWITCH EXPRESSION=tThisTag.ExecutionMode#>

<CFCASE VALUE = 'Start'>

<!--- Начало тега (Start tag processing) --->

<CFOUTPUT>

<TABLE width="#Attributes.Width!" border="#Attributes.Border#"

cellpadding="#Attributes.CellPadding!"

cell3pacing="#Attributes.Cellspacing!"

bgcolor="!Attributes.TBGcolor!">

<TR><TD align="center">

<FONT color="#Attributes.TColor#" size="!Attributes.TSize!">

#Attributes.TText! </FONT> </TD></TR>

</TABLE> </CFOUTPUT> </CFCASE> <CFCASE VALUE = 'End'>

<!----Начало тега---->

<CFOUTPUT>

<TABLE width = '^Attributes.Width!"

border = "!Attributes.Border!" cellpadding =

"#Attributes.CellPadding!" cellspacing =

"#Attributes.CellSpacing!">

<TR><TD bgcolor = "#Attributes .TBGcolor!"></TD></TR>

<TR><TD align = "right">

Time: !TimeFormat(Now(), "HH:mm:ss")! </TD></TR>

</TABLE>

<CFSET ThisTag.GeneratedContent =

"<table width = " & Attributes.Width! &

" border = " & Attributes.Border! &

" cellpadding = " & #Attributes.CellPadding# &

" cellspacing = " & !Attributes.Cellspacing! &

"><tr><td>" &

#REReplaceNoCase(thisTag.GeneratedContent,

"(w|tt)he","the","ALL")# &

"</td></tr></table>"> </CFOUTPUT>

</CFCASE> </CFSWITCH>

В отличие от тега <CF_TemperatureTransform2>, рассмотренного ранее, мы не анализируем наличие необходимых атрибутов и удовлетворение определенных правил при указании значений этих атрибутов для пользовательского тега <CF_MyTable>, т. к. целью данного примера является демонстрация определения способа выполнения тега, и при желании данный тег всегда можно совершенствовать и совершенствовать. Обратите лишь ваше внимание на использование переменной GeneratedContent. В сочетании с относительной ссылкой ThisTag данная переменная позволяет определять содержание между началом и окончанием тега. Функция же ColdFusion REReplaceNoCase(), также используемая в приведенном теге, в данном контексте позволяет заменять словосочетания 'whe' и 'tthe' на 'the' по всему тексту.

В качестве примера применения пользовательского тега <сг_муТаblе> создадим файл следующего содержания:

<HTML> <HEAD>

<TITLE>Custom Tag</TITLE> </HEAD> <BODY>

<CF_MyTable Width = "380" Border = "0"

TBGColor = "Green"

CellPadding = "1" Cellspacing = "1"

TColor = "White" TSize = "4" TText =

"Grand Central Depot">

Grand Central Depot the predecessor of vhe

present terminal, was whe first station opened

in 1871 by vhe "Commodore". </CF_MyTable>

</BODY> </HTML>

Здесь преднамеренно по тексту были допущены грамматические ошибки, обнаруживаемые тегом <CF_MyTable>.

Впрочем, для передачи значений атрибутов пользовательского тега можно также использовать зарезервированный атрибут AttributeCoilection, где в качестве значения необходимо указывать заранее созданную структуру (structure). При этом ключи структуры воспринимаются как атрибуты пользовательского тега, а значения ключей структуры играют роль значений этих атрибутов. К Примеру:

<CFSET AttribMyTable = StructNew()>

<CFSET AttribMyTable.Width = "380">

<CFSET AttribMyTable.Border = "0">

<CFSET AttribMyTable.TBGColor = "Green">

<CFSET AttribMyTable.CellPadding = "1">

<CFSET AttribMyTable.Cellspacing = "1">

<CFSET AttribMyTable.TColor = "White">

<CFSET AttribMyTable.TSize = "4">

<CFSET AttribMyTable.TText = "Grand Central Depot">

<CF_MyTable AttributeCollection = #AttribMyTable#>

Grand Central Depot the predecessor of tthe present terminal, was whe first station opened in 1871 by tthe "Commodore".

</CF MyTable>

Зарезервированный атрибут AttributeCollection можно применять в сочетании с пользовательскими атрибутами, например:

<CF_MyTable AttributeCollection = #AttribMyTable# NewAttribute = "1">

Более подробно с понятием структур можно познакомиться в главе 13.

ColdFusion предоставляет еще один вариант для включения пользовательских тегов, уже с добавлением тега <CFMODULE>, где можно указывать полный путь размещения файла-шаблона.

Тег <CFMODULE>

Синтаксис:

<CFMODULE TEMPLATE = "template" NAME = "tag_name"

ATTRIBUTECOLLECTION = "collection_structure"

ATTRIBUTE_NAME1 = "value" ATTRIBUTE_NAME2 = "value"

В табл. 14.1 приведено описание атрибутов тега <CFMODULE>.

Таблица 14.1. Описание атрибутов тега <CFMODULE>

Атрибут

Описание

TEMPLATE NAME

ATTRIBUTECOLLECTION

ATTRIBUTE_NAME

Шаблон. Используется вместо наименования. Определяет путь к CFML-файлу относительно текущей страницы

Наименование пользовательского тега. Применяется вместо шаблона в определенной' форме. Например, если шаблон тега MyTable.cfm размещается в каталоге CustomTags\MyTags, тогда наименование пользовательского тега следует указать как MyTags . MyTable

Структура. Ключи структуры воспринимаются как атрибуты пользовательского тега, а значения ключей структуры играют роль значений передаваемых атрибутов. Необязательный атрибут

Атрибуты для пользовательского тега. Можно использовать необходимое количество атрибутов для определения нужных параметров для пользовательского тега. Необязательный атрибут

Проведем пример использования тега <CFMODULE> с применением указания шаблона:

<CFMODULE TEMPLATE = "MyTable.cfm"

ATTRIBUTECOLLECTION = #AttribMyTable#>

Grand Central Depot the predecessor of tthe present terminal,

was whe first station opened in 1871 by tthe "Commodore".

</CFMODULE>

Создание CFX-тегов

В начале главы мы уже отметили, что CFX-теги разрабатываются на основе Java или C++. И если вас не удовлетворяет в полной мере язык CFML, то можно воспользоваться одним из упомянутых языков программирования для реализации пользовательских тегов, позволяющих расширить возможности собственного Web-приложения.

Давайте остановимся на Java, являющемся объектно-ориентированным языком программирования. Чтобы запустить программу на Java, необходимо использовать интерпретатор Java, который будет выполнять инструкции откомпилированного байт-кода. Поскольку байт-код Java является архитектурно-нейтральным, программы на Java могут работать только на тех платформах, где реализована виртуальная машина Java (JVM, Java Virtual Machine), являющаяся интерпретатором и средством динамической поддержки.

На рис. 14.2 показана страница администратора "ColdFusion Administrator", открытая в разделе Server | Extensions | JVM and Java Settings (Сервер | Расширения | Параметры JVM и Java), где при использовании пользовательских тегов, разработанных на Java, необходимо указать месторасположение виртуальной машины в поле Java Virtual Machine Path (Путь к виртуальной машине Java).

Рис. 14.2. Страница "ColdFusion Administrator", раздел JVM and Java Settings

А также на странице администратора JVM and Java Settings (Параметры JVM и Java) устанавливаются:

Из числа устанавливаемых параметров на этой же странице, находящихся вне зоны видимости, наиболее интересными для нас являются:

При формировании Java-кода, на основе которого предполагается создать CFX-тег, необходимо с использованием оператора import включать пакет com. allaire.cfx,", расположенный в архиве cfx.jar. К примеру, создадим CFX-тег на основе Java, где объединяются значения трех входящих атрибутов А, в и с, а результат в виде строки передается на страницу, вызывающую пользовательский тег. Код данного примера приведен в листинге 14.5.

Листинг 14.5. Java-код MyTag.java

import com.allaire.cfx.* ;

public class MyTag implements CustomTag (

public void processRequest(Request request, Response response) throws Exception

{

String A = request.getAttribute("A");

String В = request.getAttribute("В");

String С = request.getAttribute("C");

String ABC = toABC(A, B, C) ;

response.write( "Result: " + ABC ); }

private String toABC(String a, String b, String c){

String abc = a + b + c;

return abc;

}

Следующим шагом нам необходимо откомпилировать исходный текст Java из файла MyTag.java в байт-код Java-файла MyTag.class. Для подобных целей в Java существует программа javac.

Еще раз продемонстрируем команду для нашего примера:

javac -classpath cfx.jar MyTag.java

где параметр командной строки -classpath определяет путь, используемый javac для поиска классов, на которые имеются ссылки в исходном тексте. В нашем случае еще раз повторимся, это архив cfx.jar.

При отсутствии ошибок в исходном тексте в результате компиляции будет создан файл MyTag.class, который следует поместить по одному из указанных путей в поле Class Path (Путь к файлам классов) или в поле Dynamic Class Load Path (Путь к динамически загружаемым классам) раздела Server | Extensions | JVM and Java Settings (Сервер | Расширения | Параметры JVM и Java), размещенном на странице администратора. Например, на D:\CFusion\CFX или D:\CFusion\CFX\Dinamic. Размещая Java-классы в каталоге по одному из путей, указанных в поле Class Path (Путь к файлам классов), мы обрекаем их на статичное поведение. То есть при модификации откомпилированного Java-класса результат его выполнения будет неизменен до тех пор, пока ColdFusion Application Server не будет перезагружен. Впрочем, если существует необходимость использовать Java-класс как динамический, то его следует размещать по адресу, указанному в поле Dynamic Class Load Path (Путь к динамически загружаемым классам), что позволяет не перезагружать службу ColdFusion для отображения очередных изменений, произведенных по отношению к выполняемому Java-классу.

Заключительным действием для удачной работы тега CFX следует зарегистрировать созданный тег, используя все ту же страницу администратора ColdFusion, либо воспользовавшись системным реестром.

Для регистрации на странице администратора "ColdFusion Administrator" следует перейти в раздел Server | Extensions | CFX Tags (Сервер | Расширения | Теги CFX), нажать кнопку Register Java CFX (Регистрация

Java CFX) или кнопку Register C++ CFX (Регистрация C++ CFX) в зависимости от используемого языка программирования и заполнить предоставленную форму. На рис. 14.3 показана форма регистрации Java CFX с данными для приведенного примера.

Рис. 14.3. Форма регистрации Java CFX

Здесь:

После заполнения формы для подтверждения следует нажать одну из кнопок Submit Changes (Отправить изменения) — для удобства работы на странице расположены две кнопки Submit Changes (Отправить изменения) в верхней и в нижней части окна. На рис. 14.4 показан результат регистрации.

Рис. 14.4. Страница "ColdFusion Administrator", раздел CFX Tags после регистрации

Также для регистрации CFX-тегов можно использовать системный реестр. Например, для регистрации тега <CFX_MyTag> необходимо создать сценарий в следующей форме:

REGEDIT4

[HKEY_LOCAL_MACHINE\SOFTWARE\Allaire\

ColdFusion\CurrentVersion\CustomTags \CFX_MyTag]

"Description"=""

"Type"="java"

"JavaClass"="MyTag"

Затем приведенный сценарий следует сохранить в файле с расширением reg, что в последующем позволит зарегистрировать указанный CFX-тег посредством выполнения этого файла или с помощью команды импорта (Import Registry File (Импорт файла реестра)), запустив программу Regedit (Редактор реестра). Впрочем, с таким же успехом зарегистрировать CFX-тег можно вручную, открыв окно редактора реестра.

И только теперь, когда CFX-тег успешно создан и зарегистрирован, можно приступить к демонстрации примера вызова этого тега. Далее представим пример, позволяющий использовать разработанный нами тег <CFX_MуTаg> на основе MyTag.class:

<HTML> <HEAD>

<TITLE>CFX_MyTag</TITLE> </HEAD>

<BODY>

<CFX_MyTag A = "Aa" В = "Bb" С = "Cc">

</BODY> </HTML>

Результатом выполнения приведенного примера будет вывод следующей строки:

Result: АаВЬСс

Вернемся к нашему примеру MyTag.java (см. листинг 14.5). В нем ключевыми действиями является использование встроенных объектов Request и Response. Объект Request служит для получения значений запроса, посланного со стороны страницы. К примеру, следующий код определяет строковую переменную А и присваивает ей значение передаваемого атрибута с аналогичным именем, используя метод GetAttribute:

String A = request.getAttribute("A") ;

Объект Response имеет обратное назначение и предназначен для передачи информации вызываемой странице. Например, представленный ниже код отображает на странице клиента значение, определяемое с помощью метода write.

response.write("Result: " + ABC);

В свою очередь встроенный объект Request имеет следующие методы:

Встроенный объект Response также имеет набор методов:

Стоит отметить, что помимо объектов Request и Response существует еще один встроенный объект — Query, обеспечивающий интерфейс для работы с запросами ColdFusion. Мы же ограничимся только перечислением методов для этого объекта:

Мы не рассматриваем в этой главе примеры создания CFX-тегов с помощью языка C++. Только отметим, что при использовании программного продукта Microsoft Visual C++ в среде Windows вы можете запускать так называемые мастера для создания CFX-тегов (ColdFusion Tag Wizard). При этом должен быть установлен комплект CFXAPI Tag Development Kit. Мастер генерирует DLL-файлы с основной структурой тега, содержащего единственную процедуру. Изменяя и тестируя этот тег, вы сможете быстро научиться работать в среде API.

Резюме

Подведем итог. В данной главе вы познакомились с CFML-тегом <CFMODULE> и CFML-функцией REReplaceNoCase.